package com.ruoyi.business.service;

import com.alibaba.fastjson2.JSONObject;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.business.PayCallBack;
import com.ruoyi.business.PayNotifyRequest;
import com.ruoyi.business.RefundNotifyRequest;
import com.ruoyi.business.bo.Amount;
import com.ruoyi.business.bo.PayBO;
import com.ruoyi.business.bo.PayResultBO;
import com.ruoyi.business.bo.RefundResult;
import com.ruoyi.business.mapper.RefundMapper;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;


/**
 * @title: PayService
 * @copyright: copyright (c) 2021
 * @author: jiangguanjun
 * @date: 2023/10/22 17:48
 * @firstReview:
 * @lastReview:
 * @desc: 支付相关
 */
@Slf4j
@Component
public class PayService {

    /**
     * 商户号
     */
    @Value("${weixinpay.merchanid}")
    private String merchantId ;

    /**
     *  商户api证书的证书序列号
     */
    @Value("${weixinpay.mch-serial-no}")
    private String merchantSerialNumber;

    /**
     * 商户APIv3密钥
     */
    @Value("${weixinpay.api-v3-key}")
    private String apiV3key  ;

    /**
     * 商户APIv3密钥
     */
    @Value("${weixinpay.appid}")
    private String appid  ;

    @Value("${weixinpay.notify_base_url}")
    private String notifyBaseUrl  ;

    @Value("${weixinpay.pay_base_url}")
    private String payBase  ;

    @Value("${weixinpay.query_url}")
    private String queryUrl  ;

    @Value("${weixinpay.refund_url}")
    private String refundUrl  ;

    @Autowired
    private RestTemplate wxPayRestTemplate ;

    @Autowired
    private PrivateKey privateKey ;

    @Autowired
    private AutoUpdateCertificatesVerifier verifier;

    @Autowired
    private RefundMapper refundMapper ;





    public PayResultBO pay(PayBO payBO) {
        PayResultBO payResultBO = new PayResultBO() ;
        try {
            JSONObject jsonObject = new JSONObject( ) ;
            jsonObject.put("mchid",merchantId) ;
            log.info("out_trade_no:::::::::::{}",payBO.getOutTradeNo());
            jsonObject.put("out_trade_no",payBO.getOutTradeNo()) ;
            jsonObject.put("appid",appid) ;
            jsonObject.put("description",payBO.getDescription()) ;
            jsonObject.put("notify_url",notifyBaseUrl+payBO.getNotifyUrl()) ;
            Amount amount = new Amount() ;
            amount.setTotal(payBO.getTotal()*100);
            // 测试暂时写死1分钱
            amount.setTotal(1);
            jsonObject.put("amount",amount) ;
            JSONObject payer = new JSONObject() ;
            payer.put("openid","oSSrb58nv-0ISVCuK1fRqrydFMXg") ;

            jsonObject.put("payer",payer) ;

            // ========================

            HttpHeaders httpHeaders = new HttpHeaders() ;
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            String req = jsonObject.toJSONString() ;
            HttpEntity httpEntity = new HttpEntity(req,httpHeaders) ;

            String res = wxPayRestTemplate.postForObject("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",httpEntity,String.class) ;
            JSONObject resJson = JSONObject.parseObject(res) ;
            String prepayId = resJson.getString("prepay_id") ;
            // 组装前端参数 //时间戳
            long timestamp = System.currentTimeMillis();
            //随机字符串
            String nonceStr = WXPayUtil.generateNonceStr();
            //订单详情扩展字符串
            String pack ="prepay_id="+ prepayId;
            //签名
            String paySign = getSign(appid, timestamp, nonceStr, pack);
            log.info("paySignp:{}",paySign);


            payResultBO.setAppid(appid);
            payResultBO.setTimeStamp(timestamp+"");
            payResultBO.setNonceStr(nonceStr);
            payResultBO.setPack(pack);
            payResultBO.setSignType("RSA");
            payResultBO.setPaySign(paySign);
        }catch (Exception e){
            log.error("下单异常：：：",e);
            return null ;
        }

        return payResultBO ;
    }

    /**
     * @author jiangguanjun
     * @date 2023/10/23 11:08
     * @param 
     * @return 
     * @desc
     */
    public PayNotifyRequest getNotifyData(HttpServletRequest request){
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String body = "";
        try {
            StringBuilder sb = new StringBuilder();
            BufferedReader br = request.getReader();
            String str;
            while((str = br.readLine()) != null){
                sb.append(str);
            }
            body = sb.toString();
        } catch (Exception e) {
            log.info("小程序支付回调读取 body 异常", e);
        }
        log.info("小程序支付回调, Wechatpay-Signature:{}, Wechatpay-Serial:{}, Wechatpay-Timestamp:{}, Wechatpay-Nonce:{}, body:{}",
                signature, serial, timestamp, nonce, body);
        Map<String, String> result = new HashMap<>(2);
        try {

            // 1 签名验证，验证是否为微信服务器发送的报文(使用微信平台公钥对签名串和签名进行验签)
            String text = timestamp + "\n" + nonce + "\n" + body + "\n";
            if (!verifier.verify(serial, text.getBytes("utf-8"), signature))  {
                log.info("小程序支付回调验签失败，text：{}，signature：{}，serial：{}", text, signature, serial);

                return null;
            }

            // 2 解密
            PayCallBack payCallBack = JSONObject.parseObject(body, PayCallBack.class);
            if (payCallBack != null && payCallBack.getResource() != null) {
                PayCallBack.Resource resource = payCallBack.getResource();
                String decrypt = new AesUtil(apiV3key.getBytes("utf-8")).decryptToString(resource.getAssociated_data().getBytes(),
                        resource.getNonce().getBytes(), resource.getCiphertext());
                log.info("小程序支付回调，解密后信息：{}", decrypt);
                PayNotifyRequest notifyRequest = JSONObject.parseObject(decrypt, PayNotifyRequest.class);
                // 2.1 保存通知内容(非必须)

                // 3 根据解密后的信息更新订单状态
                // updateOrderStatus();
                return notifyRequest ;
            }
        } catch (Exception e) {
            log.info("小程序支付回调异常", e);
            result.put("code","FAIL");
            result.put("message","失败");

        }
        // 4 响应
        log.info("小程序支付回调，响应结果：{}", result);
        return null;
    }

    /**
     * @author jiangguanjun
     * @date 2023/10/23 11:08
     * @param
     * @return
     * @desc
     */
    public RefundNotifyRequest getRefundNotifyData(HttpServletRequest request){
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String body = "";
        try {
            StringBuilder sb = new StringBuilder();
            BufferedReader br = request.getReader();
            String str;
            while((str = br.readLine()) != null){
                sb.append(str);
            }
            body = sb.toString();
        } catch (Exception e) {
            log.info("小程序支付回调读取 body 异常", e);
        }
        log.info("小程序支付回调, Wechatpay-Signature:{}, Wechatpay-Serial:{}, Wechatpay-Timestamp:{}, Wechatpay-Nonce:{}, body:{}",
                signature, serial, timestamp, nonce, body);
        Map<String, String> result = new HashMap<>(2);
        try {

            // 1 签名验证，验证是否为微信服务器发送的报文(使用微信平台公钥对签名串和签名进行验签)
            String text = timestamp + "\n" + nonce + "\n" + body + "\n";
            if (!verifier.verify(serial, text.getBytes("utf-8"), signature))  {
                log.info("小程序支付回调验签失败，text：{}，signature：{}，serial：{}", text, signature, serial);

                return null;
            }

            // 2 解密
            PayCallBack payCallBack = JSONObject.parseObject(body, PayCallBack.class);
            if (payCallBack != null && payCallBack.getResource() != null) {
                PayCallBack.Resource resource = payCallBack.getResource();
                String decrypt = new AesUtil(apiV3key.getBytes("utf-8")).decryptToString(resource.getAssociated_data().getBytes(),
                        resource.getNonce().getBytes(), resource.getCiphertext());
                log.info("小程序支付回调，解密后信息：{}", decrypt);
                RefundNotifyRequest notifyRequest = JSONObject.parseObject(decrypt, RefundNotifyRequest.class);
                // 2.1 保存通知内容(非必须)

                // 3 根据解密后的信息更新订单状态
                // updateOrderStatus();
                return notifyRequest ;
            }
        } catch (Exception e) {
            log.info("小程序退款回调异常", e);
            result.put("code","FAIL");
            result.put("message","失败");

        }
        // 4 响应
        log.info("小程序退款回调，响应结果：{}", result);
        return null;
    }


    public String getSign(String appId, long timestamp, String nonceStr, String pack) throws Exception{
        String message = buildMessage(appId, timestamp, nonceStr, pack);
        String paySign= sign(message.getBytes("utf-8"));
        return paySign;
    }

    private String buildMessage(String appId, long timestamp, String nonceStr, String pack) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + pack + "\n";
    }
    private String sign(byte[] message) throws Exception{
        Signature sign = Signature.getInstance("SHA256withRSA");
        //这里需要一个PrivateKey类型的参数，就是商户的私钥。
        //获取商户私钥
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }



}
